from rest_framework import serializers
from .models import User
import re
# 建立django与redis的联系
from django_redis import get_redis_connection
from rest_framework_jwt.settings import api_settings
from celery_tasks.email.tasks import email_active_send

# 一般使用ModelSerializer是最好的方法，但是内部封装的代码没有密码加密，'password2','sms_code'，'allow'没有，不会保存
# 所以做用户注册的时候不用模型类序列化器
# class UserRegisterSerializer(serializers.ModelSerializer):
#     class Mate:
#         model=User
#         fields=['username','password','password2','sms_code','mobile','allow']
class UserRegisterSerializer(serializers.Serializer):
    #不使用模型类序列化器ModelSerializer的原因：
    #1.多个参数并不存在于模型类的属性中
    #2.创建对象时需要将密码加密，但是封装的create方法并没有做这件事
    #当前是注册逻辑，只需要考虑反序列化，不需要考虑序列化

    #输出jwt口令，不接收客户端输入
    token = serializers.CharField(read_only=True)

    username=serializers.CharField(
        max_length=20,
        min_length=5,
        error_messages={
            'min_length':'用户名为5--20个字符',
            'max_length':'用户名为5--20个字符'
        }
    )

    password=serializers.CharField(
        write_only=True,
        max_length=20,
        min_length=8,
        error_messages={
            'min_length':'密码为8--20个字符',
            'max_length':'密码为8--20个字符'
        }
    )

    mobile = serializers.CharField()

    password2 = serializers.CharField(write_only=True)

    # 判断验证码是否是int类型
    sms_code = serializers.IntegerField(write_only=True)

    allow = serializers.CharField(write_only=True)

    # 验证输入的用户名是否存在
    def validate_username(self, value):
        if User.objects.filter(username=value).count()>0:
            raise serializers.ValidationError('用户名存在')
        return value

    # 验证输入的手机号格式是否正确
    def validate_mobile(self,value):
        if not re.match('^1[3-9]\d{9}$',value):
            raise serializers.ValidationError('手机号格式不正确')
        return value

    # 验证用户是否同意协议条件
    def validate_allow(self,value):
        if not value:
            raise serializers.ValidationError('请同意协议')
        return value

    # 验证短信验证码是否有效  写password1和password2都不合适，attrs包含了手机号和短信验证码
    def validate(self, attrs):
        # 验证两次输入的密码是否一致
        password1 = attrs.get('password')
        password2 = attrs.get('password2')
        if password1 != password2:
            raise serializers.ValidationError('两次输入的密码不一致')

        # # 短信验证码
        # redis_cli = get_redis_connection('verify_codes')
        # key = 'sms_code_'+attrs.get('mobile')
        # sms_code_redis = redis_cli.get(key)
        # if not sms_code_redis:
        #     raise serializers.ValidationError('验证码已过期')
        # redis_cli.delete(key)
        # sms_code_redis = sms_code_redis.decode()
        # sms_code_request = attrs.get('sms_code')
        # # 验证验证码是否正确  redis数据库中获取的和用户输入的是否一致
        # if sms_code_redis != sms_code_request:
        #     raise serializers.ValidationError('验证码错误')

        # 判断短信码是否正确
        mobile = attrs.get('mobile')
        sms_code = attrs.get('sms_code')
        redis_cli = get_redis_connection('sms')
        # 1.从redis中读取，sms_手机号
        sms_code_redis = redis_cli.get('sms_' + mobile)
        # 2.判断是否过期
        if sms_code_redis is None:
            raise serializers.ValidationError('短信验证码已经过期')
        # 2.判断两个验证码是否一致
        if sms_code != int(sms_code_redis):
            raise serializers.ValidationError('短信验证码错误')


        return attrs

    # 注册--创建
    def create(self, validated_data):
        #validated_data满足验证的请求数据
        #1.从字典中获取数据
        username = validated_data.get('username')
        password = validated_data.get('password')
        mobile = validated_data.get('mobile')
        # 2.创建对象并保存
        user = User()
        user.username = username
        # 直接保存密码，未加密  只有三个值需要存，其他值不需要长久存储
        # user.password=password
        # 通过阅读官方文档知道这个方法，可以对密码加密后再保存到属性上
        # set_password会自动加密
        user.set_password(password)
        user.mobile = mobile
        user.save()

        # return {   # 返回值从对象中找属性的时候可能会出错，会从序列化器中做序列化输出
        #     'id':user.id,
        #     'username':user.username,
        #     'mobile':user.mobile
        # }

        # 登录之后的状态保持（一种是session（依赖于浏览器）、一种是token（适配于各种客户端，信息可以不在服务器端保存）
        # 云通信中带有token进行身份验证
        # jwt是token的一种方案、json格式，jwt是最常用的方案  口令（本质就是一个字符串）用于合适身份的
        # jwt本质就是一个字符串，由三部分构成（jwt.io官网可看）：
            # header（头）json转出的一个数据、
            # payload（载荷）、
            # signature（签名） 加密（私钥，只有服务器知道），不能解密  可以更改header来更改signature

        #1.获取jwt的方法
        #
        jwt_payload_handler = api_settings.JWT_PAYLOAD_HANDLER
        jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
        #2.生成载荷  接收user参数生成载荷
        payload = jwt_payload_handler(user)
        #3.生成口令
        token = jwt_encode_handler(payload)
        # 将口令添加到user对象上，输出
        user.token = token

        return user

# 查询与模型类对应
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        # 操作值的属性 要返回值的josn属性
        fields = ['id','username','mobile','email','email_active']


class UserEmailSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ['id', 'email']

        # def validate_email(self,value):
            # 正则表达式
            # return value

    # 默认update方法修改所有属性，此时只需要修改邮箱，所以重写
    def update(self,instance,validated_data):
        instance.email = validated_data.get('email')
        email = validated_data.get('email')
        instance.email = email
        instance.save()

        #发邮件：调用方法，向邮件服务器发消息，耗时，写到celery中
        #生成验证链接   激活地址 + 用户邮箱（参数）
        verify_url = 'http://www.mieduo.site:8080/success_verify_meail.html?token=' + instance.email_dumps()
        #发送验证邮件
        email_active_send.delay(email,verify_url)

        return instance
